package com.night.icm.service.business.service;

import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.night.icm.common.jpa.PageHelper;
import com.night.icm.common.service.PageDataUtil;
import com.night.icm.common.utils.PageDataDTO;
import com.night.icm.model.business.ConsolidatedResultsDTO;
import com.night.icm.model.business.DataSeriesDTO;
import com.night.icm.persistence.base.entity.Academy;
import com.night.icm.persistence.base.entity.Class;
import com.night.icm.persistence.base.entity.Profession;
import com.night.icm.persistence.base.entity.Student;
import com.night.icm.persistence.business.entity.AcademicProgress;
import com.night.icm.persistence.business.entity.ConsolidatedResults;
import com.night.icm.persistence.business.entity.StudentCreditRecord;
import com.night.icm.persistence.business.reporsitory.ConsolidatedResultsRepository;
import com.night.icm.persistence.business.reporsitory.StudentCreditRecordRepository;
import com.night.icm.service.business.mapper.ConsolidatedResultsMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

/**
 * StatisticalAnalysisServiceImpl
 *
 * @author Liao ZhiYong
 * @date 2020/4/1
 */
@Slf4j
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisService {

    private final ConsolidatedResultsRepository consolidatedResultsRepository;
    private final ConsolidatedResultsMapper consolidatedResultsMapper;
    private final StudentCreditRecordRepository studentCreditRecordRepository;

    @Autowired
    public StatisticalAnalysisServiceImpl(ConsolidatedResultsRepository consolidatedResultsRepository, ConsolidatedResultsMapper consolidatedResultsMapper, StudentCreditRecordRepository studentCreditRecordRepository) {
        this.consolidatedResultsRepository = consolidatedResultsRepository;
        this.consolidatedResultsMapper = consolidatedResultsMapper;
        this.studentCreditRecordRepository = studentCreditRecordRepository;
    }

    /**
     * 分页查找学生综合成绩列表
     *
     * @param page         页号，从0开始
     * @param size         每页记录条数
     * @param sort         排序字段，例如：字段1,asc,字段2,desc
     * @param academyId    学院名称
     * @param professionId 专业名称
     * @param classId      班级名称
     * @param studentName  学生姓名
     * @param year         测评年份
     * @return 学生综合成绩列表
     */
    @Override
    public PageDataDTO<ConsolidatedResultsDTO> findOnePage(int page, int size, String sort, String academyId, String professionId, String classId, String studentName, String year) {
        PredicateBuilder<ConsolidatedResults> pb = Specifications.<ConsolidatedResults>and()
                .eq(StringUtils.isNotBlank(academyId), "academyId", academyId)
                .eq(StringUtils.isNotBlank(professionId), "professionId", professionId)
                .eq(StringUtils.isNotBlank(classId), "classId", classId)
                .like(StringUtils.isNotBlank(studentName), "studentName", "%" + studentName + "%")
                .eq(StringUtils.isNotBlank(year), "year", year);
        Page<ConsolidatedResults> all = this.consolidatedResultsRepository.findAll(pb.build(), PageHelper.generatePageRequest(page, size, sort));
        List<ConsolidatedResultsDTO> list = this.consolidatedResultsMapper.toList(all.getContent());
        return PageDataUtil.toPageData(all, list);
    }

    /**
     * 统计与分析学生综合成绩情况
     *
     * @param academyId        学院名称
     * @param professionId     专业名称
     * @param classId          班级名称
     * @param year             测评年份
     * @param standard         达标分数
     * @param notStandard      未达标分数
     * @param earlyWarningDown 预警下限
     * @param earlyWarningUp   预警上限
     * @return 学生综合成绩情况
     */
    @Override
    public List<DataSeriesDTO> analysisConsolidatedResults(String academyId, String professionId, String classId, String year, Double standard, Double notStandard, Double earlyWarningDown, Double earlyWarningUp) {
        List<DataSeriesDTO> dataSeriesList = new ArrayList<>(4);
        PredicateBuilder<ConsolidatedResults> pb = Specifications.<ConsolidatedResults>and()
                .eq(StringUtils.isNotBlank(academyId), "academyId", academyId)
                .eq(StringUtils.isNotBlank(professionId), "professionId", professionId)
                .eq(StringUtils.isNotBlank(classId), "classId", classId)
                .eq(StringUtils.isNotBlank(year), "year", Integer.valueOf(year));
        List<ConsolidatedResults> all = this.consolidatedResultsRepository.findAll(pb.build());
        long standardTotal = 0;
        long notStandardTotal = 0;
        long earlyWarningTotal = 0;
        long ordinary = 0;
        for (ConsolidatedResults consolidatedResults : all
        ) {
            if (consolidatedResults.getConsolidatedResult() >= standard) {
                standardTotal++;
            } else if (consolidatedResults.getConsolidatedResult() < notStandard) {
                notStandardTotal++;
            } else if (consolidatedResults.getConsolidatedResult() >= earlyWarningDown && consolidatedResults.getConsolidatedResult() <= earlyWarningUp) {
                earlyWarningTotal++;
            } else {
                ordinary++;
            }
        }
        dataSeriesList.add(new DataSeriesDTO("达优人数", standardTotal));
        dataSeriesList.add(new DataSeriesDTO("未达标人数", notStandardTotal));
        dataSeriesList.add(new DataSeriesDTO("预警人数", earlyWarningTotal));
        dataSeriesList.add(new DataSeriesDTO("良好人数", ordinary));
        return dataSeriesList;
    }

    /**
     * 统计与分析毕业学分修读情况
     *
     * @param academyId        学院名称
     * @param professionId     专业名称
     * @param classId          班级名称
     * @param grade            所属年级
     * @param standard         达标分数
     * @param notStandard      未达标分数
     * @param earlyWarningDown 预警下限
     * @param earlyWarningUp   预警上限
     * @return 毕业学分修读情况
     */
    @Override
    public List<DataSeriesDTO> analysisGraduationCredits(String academyId, String professionId, String classId, String grade, Double standard, Double notStandard, Double earlyWarningDown, Double earlyWarningUp) {
        List<DataSeriesDTO> dataSeriesList = new ArrayList<>(4);
        Specification<StudentCreditRecord> specification = ((Root<StudentCreditRecord> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
            List<Predicate> conditions = new ArrayList<>();
            if (!StringUtils.isAllBlank(academyId, professionId, classId) || grade != null) {
                if (grade != null) {
                    conditions.add(cb.and(cb.equal(root.get("grade").as(Integer.class), Integer.valueOf(grade))));
                }
                Join<StudentCreditRecord, AcademicProgress> academicProgressJoin = root.join("academicProgress", JoinType.LEFT);
                Join<AcademicProgress, Student> studentJoin = academicProgressJoin.join("student", JoinType.LEFT);
                Join<Student, Academy> academyJoin = studentJoin.join("academy", JoinType.LEFT);
                Join<Student, Profession> professionJoin = studentJoin.join("profession", JoinType.LEFT);
                Join<Student, Class> classJoin = studentJoin.join("aClass", JoinType.LEFT);
                if (StringUtils.isNotBlank(academyId)) {
                    conditions.add(cb.and(cb.equal(academyJoin.get("id").as(Long.class), Long.valueOf(academyId))));
                }
                if (StringUtils.isNotBlank(professionId)) {
                    conditions.add(cb.and(cb.equal(professionJoin.get("id").as(Long.class), Long.valueOf(professionId))));
                }
                if (StringUtils.isNotBlank(classId)) {
                    conditions.add(cb.and(cb.equal(classJoin.get("id").as(Long.class), Long.valueOf(classId))));
                }
            }
            if (!conditions.isEmpty()) {
                query.where(conditions.toArray(new Predicate[0]));
            }
            return null;
        });
        List<StudentCreditRecord> all = this.studentCreditRecordRepository.findAll(specification);
        long standardTotal = 0;
        long notStandardTotal = 0;
        long earlyWarningTotal = 0;
        long ordinary = 0;
        for (StudentCreditRecord studentCreditRecord : all
        ) {
            if (studentCreditRecord.getTotal() >= standard) {
                standardTotal++;
            } else if (studentCreditRecord.getTotal() < notStandard) {
                notStandardTotal++;
            } else if (studentCreditRecord.getTotal() >= earlyWarningDown && studentCreditRecord.getTotal() <= earlyWarningUp) {
                earlyWarningTotal++;
            } else {
                ordinary++;
            }
        }
        dataSeriesList.add(new DataSeriesDTO("达优人数", standardTotal));
        dataSeriesList.add(new DataSeriesDTO("未达标人数", notStandardTotal));
        dataSeriesList.add(new DataSeriesDTO("预警人数", earlyWarningTotal));
        dataSeriesList.add(new DataSeriesDTO("良好人数", ordinary));
        return dataSeriesList;
    }
}